//----------------------------------------------------------------------------
// M A R I T I M E  R E S E A R C H  I N S T I T U T E  N E T H E R L A N D S
//----------------------------------------------------------------------------
// Copyright (C) 2008 - MARIN - All rights reserved - http://www.marin.nl
//----------------------------------------------------------------------------
// Program    : mermaid
// Module     : vbm
// File       : Array1D.h
//----------------------------------------------------------------------------

#ifndef ARRAY1D_H
#define ARRAY1D_H

#include <string.h> // memcpy
#ifdef UNITTEST
#include <gtest/gtest.h> // FRIEND_TEST
#endif // UNITTEST
#include "assertplus.h"

template <class T> class Array1Diterator;

/// one dimensional array class that is consistent with the Array2D class
template <class T>
class Array1D
{
#ifdef UNITTEST
    FRIEND_TEST(Array1D, default_constructor);
    FRIEND_TEST(Array1D, size_constructor);
    FRIEND_TEST(Array1D, copy);
#endif

    friend class Array1Diterator<T>;
    
public:

    /// default constructor
    Array1D()
        : m_Size(0)
        , m_pElements(0)
    {
    }

    /// constructor to create array of specified size
    Array1D(unsigned size)
        : m_Size(size)
        , m_pElements(0)
    {
        m_pElements = new T[size];
    }

    /// copy constructor
    Array1D(const Array1D& m)
        : m_Size(m.size())
        , m_pElements(0)
    {
        copy(m);
    }

    /// destructor
    virtual ~Array1D()
    {
        delete [] m_pElements;
    }

    /// assignment operator
    Array1D & operator=(const Array1D& m)
    {
        if (&m != this)
        {
            copy(m);
        }
        return *this;
    }

    /// assigns specified value to all elements
    Array1D & operator=(T value)
    {
        unsigned n = size();
        for (unsigned i = 0; i < n; ++i)
        {
            m_pElements[i] = value;
        }
        return *this;
    }

    /// addition
    Array1D & operator+=(T value)
    {
        unsigned n = size();
        for (unsigned i = 0; i < n; ++i)
        {
            m_pElements[i] += value;
        }
        return *this;
    }

    /// multiplication
    Array1D & operator*=(T value)
    {
        unsigned n = size();
        for (unsigned i = 0; i < n; ++i)
        {
            m_pElements[i] *= value;
        }
        return *this;
    }

    /// equality operator
    bool operator==(const Array1D& m) const
    {
        if (m_Size != m.m_Size)
        {
            return false;
        }
        return !memcmp(m_pElements, m.m_pElements, m_Size * sizeof (T));
    }

    /// inequality operator
    inline bool operator!=(const Array1D& m) const
    {
        return !(*this == m);
    }

    /// index operator
    inline T & operator[](unsigned index)
    {
        ASSERT(index < m_Size, "index=" << index << ", m_Size=" << m_Size);
        return m_pElements[index];
    }

    /// index operator (read-only)
    inline T operator[](unsigned index)const
    {
        ASSERT(index < m_Size, "index=" << index << ", m_Size=" << m_Size);
        return m_pElements[index];
    }

    /// alternative index operator
    inline T & operator()(unsigned index)
    {
        ASSERT(index < m_Size, "index=" << index << ", m_Size=" << m_Size);
        return m_pElements[index];
    }

    /// alternative index operator (read-only)
    inline T operator()(unsigned index)const
    {
        ASSERT(index < m_Size, "index=" << index << ", m_Size=" << m_Size);
        return m_pElements[index];
    }

    /// returns size of array
    inline unsigned size() const
    {
        return m_Size;
    }

    /// returns size of array
    inline unsigned dim() const
    {
        return size();
    }

    /// clears array (all elements zero)
    /// this only works for numeric types!
    inline void clear()
    {
        *this = 0;
    }

    /// resizes array to specified size (previous contents are lost)
    void resize(unsigned size)
    {
        delete [] m_pElements;
        m_Size = size;
        m_pElements = new T[size];
    }

    /// returns address
    T* getElements() const
    {
        return m_pElements;
    }
    
    /// returns begin iterator
    Array1Diterator<T> begin()
    {
        return Array1Diterator<T>(*this, 0);
    }
    
    /// returns end iterator
    Array1Diterator<T> end()
    {
        return Array1Diterator<T>(*this, size());
    }

private:

    /// copies contents of array
    void copy(const Array1D &m)
    {
        delete [] m_pElements;
        m_pElements = new T[m_Size];
        memcpy(m_pElements, m.m_pElements, m_Size * sizeof (T));
        m_Size = m.m_Size;
    }

    // data members
    unsigned m_Size; ///< number of elements
    T *m_pElements; ///< pointer to array of elements
    T *m_pIterator; ///< pointer for iterator implementation
};

/// partial specializations of clear function

//template <> void Array1D<bool>::clear()
//{
//   *this = false;
//}

//template <> void Array1D<std::string>::clear()
//{
//    *this = std::string("");
//}

template <class T> class Array1Diterator;

template <class T> bool operator==(const Array1Diterator<T>& a, const Array1Diterator<T>& b);
template <class T> bool operator!=(const Array1Diterator<T>& a, const Array1Diterator<T>& b);

template <class T> 
class Array1Diterator
{

    friend bool operator==<T>(const Array1Diterator<T>& a, const Array1Diterator<T>& b);
    friend bool operator!=<T>(const Array1Diterator<T>& a, const Array1Diterator<T>& b);

public:

    /// constructor
    Array1Diterator<T>(Array1D<T>& ar, int index) : m_array(ar), m_index(index)
    {
    }
    
    /// dereference operator
    T & operator*() 
    { 
        return m_array[m_index]; 
    }
    
    /// prefix increment oprator
    Array1Diterator<T>& operator++() 
    { 
        ++m_index; 
        return *this; 
    }
    
    /// postfix increment operator
    Array1Diterator<T> operator++(int)
    {
        Array1Diterator<T> clone(*this);
        ++m_index;
        return clone;
    }
    
  private:
    
    // data members
    Array1D<T>& m_array; /// array to work on
    int m_index; /// index of current element
};

/// equality operator
template <class T> bool operator==(const Array1Diterator<T>& a, const Array1Diterator<T>& b)
{
    return a.m_array == b.m_array && a.m_index == b.m_index;
}

/// inequality operator
template <class T> bool operator!=(const Array1Diterator<T>& a, const Array1Diterator<T>& b)
{
    return !operator==(a, b);
}

#endif // ARRAY1D_H
